package com.codejoys.monitor.service.impl;

import com.codejoys.monitor.Constants;
import com.codejoys.monitor.Utils;
import com.codejoys.monitor.dao.ChannelDao;
import com.codejoys.monitor.dao.DeviceDao;
import com.codejoys.monitor.domain.*;
import com.codejoys.monitor.dto.ConfigDto;
import com.codejoys.monitor.dto.DeviceDto;
import com.codejoys.monitor.dto.PageData;
import com.codejoys.monitor.mapper.*;
import com.codejoys.monitor.service.DeviceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.rmi.CORBA.Util;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class DeviceServiceImpl implements DeviceService {
    private static final Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class);

    @Autowired
    DeviceMapper deviceMapper;

    @Autowired
    DeviceDao deviceDao;

    @Autowired
    ConfigMapper configMapper;

    @Autowired
    ChannelMapper channelMapper;

    @Autowired
    ChannelDao channelDao;

    @Autowired
    DeviceDataMapper deviceDataMapper;

    @Autowired
    SysConfigMapper sysConfigMapper;

    @Override
    public ConfigDto findBySn(String sn) {
        // 只查询有过完整老化记录的设备
        DeviceExample example = new DeviceExample();
        example.createCriteria()
                .andStatusEqualTo((byte) Constants.Status.FINISHED.value())
                .andSnEqualTo(sn);
        example.setOrderByClause("aging_times desc");

        // 查询老化记录
        final List<Device> devices = deviceMapper.selectByExample(example);

        // 查询配置的阈值，预设电流电压
        int agingTimes = (devices != null && devices.size() > 0)
                ? devices.get(0).getAgingTimes()
                : 0;

        final int[] modelConfigs = Utils.getModelConfigs(sysConfigMapper);
        String model = Utils.getModelBySn(sn, modelConfigs[0], modelConfigs[1]);

        BigDecimal devicePower = Utils.getDevicePower(sn);

        ConfigExample ce = new ConfigExample();
        ce.createCriteria()
                .andAgingLowerLessThanOrEqualTo(agingTimes)
                .andAgingUpperGreaterThanOrEqualTo(agingTimes)   // 老化次数在指定范围之内
                .andPowerLowerLessThanOrEqualTo(devicePower)    // 功率范围
                .andPowerUpperGreaterThanOrEqualTo(devicePower)
                .andModelEqualTo(model);

        List<Config> configs = configMapper.selectByExample(ce);

        ConfigDto dto = new ConfigDto();
        dto.setAgingTimes(agingTimes);
        dto.setConfigs(configs);

        return dto;
    }

    @Override
    public Device findByChannel(String channel) {
        ChannelExample e1 = new ChannelExample();
        e1.createCriteria().andNumEqualTo(channel);
        final List<Channel> channels = channelMapper.selectByExample(e1);

        if (channels == null || channels.size() == 0) {
            return null;
        }

        // 通道id
        int channelId = channels.get(0).getId();

        // 查询未移除的设备
        DeviceExample e2 = new DeviceExample();
        e2.createCriteria()
                .andChannelIdEqualTo(channelId)
                .andIsRemovedNotEqualTo((byte) 1);

        final List<Device> devices = deviceMapper.selectByExample(e2);
        if (devices != null && devices.size() > 0) {
            return devices.get(0);
        }

        return null;
    }

    @Override
    public PageData<DeviceDto> findList(DeviceDto device, Integer page, Integer size) {
        if (page == null || page <= 0) page = 1;
        if (size == null || size <= 0) size = 10;

        if (device.getSn() != null) {
            device.setSn("%" + device.getSn() + "%");
        }

        final int total = deviceDao.countList(device);
        final List<DeviceDto> devices = deviceDao.findList(device, (page - 1) * size, size);

        return new PageData<DeviceDto>((long) total, devices);
    }

    @Override
    public List<DeviceDto> statusList(String house) {
        // 查询设备信息
        final List<DeviceDto> dtos = deviceDao.statusDeviceList(house);

        if (dtos == null || dtos.size() == 0) {
            return new ArrayList<>();
        }

        // 查询设备的最新温度、电流电压（分两次查询，性能会好一些）
        //final List<Integer> ids = dtos.stream().map(dto -> dto.getId()).collect(Collectors.toList());
        final List<Integer> ids = dtos.stream()
                // 只查询老化中的设备数据
                .filter(dto -> dto.getStatus() == Constants.Status.AGING.value())
                .map(dto -> dto.getId())
                .collect(Collectors.toList());


        // 没有老化中的设备
        if (ids == null || ids.size() == 0) return dtos;

        final List<DeviceData> deviceData = deviceDao.statusDataList(ids);
        final Map<Integer, DeviceData> dataLookup = deviceData
                .stream()
                .collect(Collectors.toMap(DeviceData::getDeviceId, c -> c));

        dtos.forEach(dto -> {
            final DeviceData data = dataLookup.get(dto.getId());
            if (data != null) {
                dto.setVoltage(data.getVoltage());
                dto.setCurrent(data.getCurrent());
                dto.setTemperature(data.getTemperature());
            }
        });

        return dtos;
    }

    /**
     * @param house 老化房
     * @return
     */
    @Override
    public List<DeviceDto> getDeviceStatus(String house) {
         return deviceDao.getDeviceStatus(house);
    }
    
    @Override
    public void updateById(Device device) {
        deviceMapper.updateByPrimaryKeySelective(device);
    }

    @Override
    public void deleteById(Integer id, Integer userId) {
        final Device device = deviceMapper.selectByPrimaryKey(id);

        if (device != null) {
            device.setDeleted((byte) 1);
            device.setUpdatedBy(userId);
            device.setUpdated(new Date());
        }

        deviceMapper.updateByPrimaryKeySelective(device);
    }

    @Override
    public void insert(List<DeviceDto> devices, Integer uid) {
        // 关联通道
        final List<Channel> channels = devices.stream()
                .map(d -> {
                    Channel c = new Channel();
                    c.setNum(d.getChannel());
                    c.setCreated(new Date());
                    c.setCreatedBy(uid);
                    c.setHouse(c.getNum().substring(0, 3)); // A0101 => A01

                    return c;
                })
                .collect(Collectors.toList());

        // 插入通道，重复的通道会忽略
        channelDao.batchInsert(channels);

        List<String> nums = channels.stream().map(c -> c.getNum()).collect(Collectors.toList());
        ChannelExample ce = new ChannelExample();
        ce.createCriteria().andNumIn(nums);

        // channel number => channel id
        final Map<String, Integer> channelLookup = channelMapper.selectByExample(ce).stream()
                .collect(Collectors.toMap(Channel::getNum, c -> c.getId()));

        // 关联通道id
        devices.forEach(d -> {
            Integer id = channelLookup.get(d.getChannel());
            if (id != null) {
                d.setChannelId(id);
            }
        });

        deviceDao.batchInsert(devices);
    }

    @Override
    public List<DeviceData> getDeviceLogs(Integer deviceId, Date start) {
        DeviceDataExample e = new DeviceDataExample();
        e.createCriteria().andDeviceIdEqualTo(deviceId).andCreatedGreaterThan(start);

        e.setOrderByClause("created");
        return deviceDataMapper.selectByExample(e);
    }

    @Override
    public List<DeviceDto> findList(DeviceDto device) {
        if (device.getSn() != null) {
            device.setSn("%" + device.getSn() + "%");
        }

        final List<DeviceDto> devices = deviceDao.findExportList(device);

        return devices;
    }
}
